/*
 * Copyright (C) 2008 Universidade Federal de Campina Grande
 *  
 * This file is part of OurGrid. 
 *
 * OurGrid is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free 
 * Software Foundation, either version 3 of the License, or (at your option) 
 * any later version. 
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public License 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.ourgrid.peer.controller;

import org.ourgrid.common.interfaces.RemoteWorkerProvider;
import org.ourgrid.common.interfaces.RemoteWorkerProviderClient;
import org.ourgrid.common.interfaces.management.RemoteWorkerManagement;
import org.ourgrid.common.interfaces.management.RemoteWorkerManagementClient;
import org.ourgrid.common.spec.worker.WorkerSpec;
import org.ourgrid.common.statistics.control.WorkerControl;
import org.ourgrid.peer.PeerConstants;
import org.ourgrid.peer.controller.allocation.RedistributionController;
import org.ourgrid.peer.controller.messages.WorkerMessages;
import org.ourgrid.peer.dao.AllocationDAO;
import org.ourgrid.peer.to.RemoteAllocableWorker;
import org.ourgrid.reqtrace.Req;

import br.edu.ufcg.lsd.commune.api.FailureNotification;
import br.edu.ufcg.lsd.commune.api.InvokeOnDeploy;
import br.edu.ufcg.lsd.commune.api.MonitoredBy;
import br.edu.ufcg.lsd.commune.api.RecoveryNotification;
import br.edu.ufcg.lsd.commune.container.servicemanager.ServiceManager;
import br.edu.ufcg.lsd.commune.identification.DeploymentID;
import br.edu.ufcg.lsd.commune.network.certification.CertificationUtils;

@Req({"REQ018"})
public class RemoteWorkerProviderClientController implements RemoteWorkerProviderClient{

	private ServiceManager serviceManager;

	@InvokeOnDeploy
	public void init(ServiceManager serviceManager) {
		this.serviceManager = serviceManager;
	}
	
	/**
	 * @param provider
	 * @param worker
	 * @param workerSpec
	 * @param senderPublicKey 
	 */
	public void hereIsWorker(
			@MonitoredBy(PeerConstants.REMOTE_WORKER_PROVIDER_CLIENT) RemoteWorkerProvider provider, 
			@MonitoredBy(PeerConstants.REMOTE_WORKER_MONITOR_OBJECT_NAME) RemoteWorkerManagement worker, 
			WorkerSpec workerSpec) {

		String senderPublicKey = serviceManager.getSenderPublicKey();
		
		if (provider == null) {
			serviceManager.getLog().warn(WorkerMessages.getReceivingNullRemoteProviderMessage(senderPublicKey));
			return;
		}

		DeploymentID providerObjectID = serviceManager.getStubDeploymentID(provider);
		if (worker == null) {
			serviceManager.getLog().warn(WorkerMessages.getReceivingNullRemoteWorkerMessage(providerObjectID));
			serviceManager.release(provider);
//			serviceManager.release(worker);
			return;
		}
		
		if (workerSpec == null) {
			serviceManager.getLog().warn(
					WorkerMessages.getReceivingNullWorkerSpecRemoteWorkerMessage(providerObjectID));
			serviceManager.release(provider);
			serviceManager.release(worker);
			return;
		}
		
		AllocationDAO allocationDAO = serviceManager.getDAO(AllocationDAO.class);
		
		DeploymentID workerObjectID = serviceManager.getStubDeploymentID(worker);
		if (allocationDAO.getRemoteAllocableWorker(workerObjectID.getPublicKey()) != null) {
			serviceManager.getLog().warn(
					WorkerMessages.getReceivingAlreadyAllocatedRemoteWorkerMessage(workerObjectID));
			return;
		}
		
		String certSubjectDN = CertificationUtils.getCertSubjectDN(serviceManager.getSenderCertPath());
		
		WorkerControl.getInstance().addRemoteWorker(workerSpec, serviceManager, certSubjectDN);
		
		serviceManager.getLog().debug(WorkerMessages.getReceivedRemoteWorkerMessage(providerObjectID, workerObjectID));
		
		
		DeploymentID workerDeploymentID = serviceManager.getStubDeploymentID(worker);
		RemoteAllocableWorker rAlloca = 
			serviceManager.getDAO(AllocationDAO.class).getRemoteAllocableWorker(workerDeploymentID.getPublicKey());

		if (rAlloca == null) {
			RemoteWorkerManagementClient rwmc = 
				(RemoteWorkerManagementClient) serviceManager.getObjectDeployment(
						PeerConstants.REMOTE_WORKER_MANAGEMENT_CLIENT).getObject();
			
			rAlloca = new RemoteAllocableWorker(workerSpec, worker, rwmc, provider, certSubjectDN, 
						providerObjectID.getServiceID(), workerDeploymentID.getServiceID());
			serviceManager.getDAO(AllocationDAO.class).
				addRemoteAllocableWorker(workerDeploymentID.getPublicKey(), rAlloca);
		
		} else {
			//TODO Log duplicated remote worker public key 
		}
		
		RedistributionController.getInstance().redistributeWorker(provider, rAlloca, serviceManager);
		
		//TODO release the provider if it does not have any worker donated to me
		
	}

	@RecoveryNotification
	public void remoteWorkerProviderIsUp(RemoteWorkerProvider provider) {
		
	}
	
	@FailureNotification
	public void remoteWorkerProviderIsDown(RemoteWorkerProvider provider) {
		serviceManager.release(provider);
	}
}
